﻿using ICSharpCode.AvalonEdit.Document;
using ICSharpCode.AvalonEdit.Folding;
using ICSharpCode.AvalonEdit.Rendering;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows.Media;

namespace NFox.Pyeditor.Runtime
{
    public class MarkerService : IBackgroundRenderer, IVisualLineTransformer
    {

        private static PythonFoldingStrategy _pfs = new PythonFoldingStrategy();

        private TextSegmentCollection<TextMarker> markers;
        private CodeEditor _code;
        private TextDocument _doc;
        private TextView _view;

        private FoldingManager _fm;
        private BreakPointMargin _bpm;
        private ErrorMarker _lasterr = null;

        private AstTree _tree = null;

        public BreakPointList BreakPoints { get { return _bpm.BreakPoints; } }

        public MarkerService(CodeEditor code)
        {
            _code = code;
            _doc = _code.Document;
            _view = _code.TextArea.TextView;
            _view.BackgroundRenderers.Add(this);
            _view.LineTransformers.Add(this);
            _view.Services.AddService(typeof(MarkerService), this);
            markers = new TextSegmentCollection<TextMarker>(_doc);
            _bpm = new BreakPointMargin(_code);
            _code.TextArea.LeftMargins.Insert(0, _bpm);
            _fm = FoldingManager.Install(code.TextArea);
        }

        #region Update

        public bool Update()
        {

            try
            {
                _tree = AstTree.Parse(_doc.Text);
                try
                {
                    ClearErrorMaker();
                    _lasterr = null;
                    return true;
                }
                catch { }

            }
            catch (Exception ex)
            {
                ErrorMarker err = new ErrorMarker(_doc, ex);
                if (err.Equals(_lasterr)) return false;
                ClearErrorMaker();
                _lasterr = err;
                Add(err);
            }
            return false;
        }

        public void UpdateFoldings()
        {
            _pfs.UpdateFoldings(_fm, _doc);
        }

        private LineColorizer _lineColorizer = null;

        public void AddDebugMarkerAt(BreakPoint pt)
        {
            _lineColorizer = new LineColorizer(pt.Line);
            _view.LineTransformers.Add(_lineColorizer);
        }

        public void ClearDebugMarker()
        {
             if (_lineColorizer != null)
                _view.LineTransformers.Remove(_lineColorizer);           
        }

        #endregion

        public void Draw(TextView textView, DrawingContext drawingContext)
        {
            if (markers == null || !textView.VisualLinesValid)
                return;
            var visualLines = textView.VisualLines;
            if (visualLines.Count == 0)
                return;

            int start = visualLines.First().FirstDocumentLine.Offset;
            int end = visualLines.Last().LastDocumentLine.EndOffset;
            int len = end - start;

            foreach (TextMarker marker in markers.FindOverlappingSegments(start, len))
                marker.Draw(textView, drawingContext);

        }

        public KnownLayer Layer
        {
            get { return KnownLayer.Selection; }
        }

        public void Transform(ITextRunConstructionContext context, IList<VisualLineElement> elements){ }

        public void ClearErrorMaker()
        {
            foreach (TextMarker m in markers.OfType<ErrorMarker>())
                Remove(m);
        }

        private void Add(TextMarker marker)
        {
            markers.Add(marker);
            Redraw(marker);
        }

        private void Remove(TextMarker marker)
        {
            if (markers.Remove(marker))
                Redraw(marker);
        }

        public void Redraw(ISegment segment)
        {
            _view.Redraw(segment);
        }

        public IEnumerable<TextMarker> GetMarkersAtOffset(int offset)
        {
            return markers == null ? Enumerable.Empty<TextMarker>() : markers.FindSegmentsContaining(offset);
        }



    }
}
